
#include "membackend/accurateRank.h"

Rank::Rank()
{
    m_numBanks = 0;
    m_numBankGroups = 0;
}

Rank::Rank(uint64_t numBanks, uint64_t numBankGroups)
{

    m_numBanks = numBanks;
    m_numBankGroups = numBankGroups;
    m_bankVector.resize(m_numBankGroups);
    for (int i = 0; i < m_numBankGroups; i++)
    {
        m_bankVector[i].resize(m_numBanks);
    }
}

int Rank::Init(AddressMappingConfig *amp)
{

    this->amp = amp;
    // IRankTiming* rank_timing = dynamic_cast<IRankTiming*>(this);

    for (int i = 0; i < m_numBankGroups; i++)
    {
        for (int j = 0; j < m_numBanks; j++)
        {
            m_bankVector[i][j].Init(i, j, this);
            // std::cout << "Bank " << j << " in BankGroup " << i << " initialized" << std::endl;
        }
    }

    return 0;
}

Bank &Rank::getBank(uint64_t bank_group_id, uint64_t bank_id)
{
    if (bank_group_id >= m_numBankGroups || bank_id >= m_numBanks)
    {
        std::cerr << "Error: Invalid bank or bank group ID: " << " Bank Group ID: " << bank_group_id
                  << ", Bank ID: " << bank_id << "m__numBankGroups: " << m_numBankGroups
                  << ", m_numBanks: " << m_numBanks << std::endl;
        std::cerr << "Error: Invalid bank or bank group ID." << std::endl;
        throw std::out_of_range("Invalid bank group or bank ID"); // 更安全的做法
    }
    return m_bankVector[bank_group_id][bank_id];
}

uint64_t Rank::getRankRRDConstrain(uint64_t bankgroup_id, uint64_t bank_id, uint64_t time)
{
    uint64_t delay = 0;
    uint64_t earliest_time = 0;
    uint64_t delay_RRD = 0;
    uint64_t delay_FAW = this->getRankFAWConstrain(bankgroup_id, bank_id, time);

    if (bankgroup_id >= m_numBankGroups || bank_id >= m_numBanks)
    {
        std::cerr << "Error: Invalid bank or bank group ID: " << " Bank Group ID: " << bankgroup_id
                  << ", Bank ID: " << bank_id << std::endl;
        throw std::out_of_range("Rank::addRW Invalid bank group or bank ID");
    }

    if (!this->getIsACT())
    {
        this->setIsACT(true);
        this->last_bank_id = bank_id;
        this->last_bankGroup_id = bankgroup_id;
    }
    else
    {
        if (this->last_bankGroup_id == bankgroup_id)
        {
            if (this->last_bank_id != bank_id)
            {
                if (amp->getTRRD_S() + this->last_act_time > time)
                {

                    delay_RRD = amp->getTRRD_S() + this->last_act_time - time;
                    std::cout << "Rank::getRankRRDConstrain: delay_RRD due to same bank group is: " << delay_RRD << std::endl;
                }
            }
        }
        else
        {
            if (amp->getTRRD_L() + this->last_act_time > time)
            {
                delay_RRD = amp->getTRRD_L() + this->last_act_time - time;
                std::cout << "Rank::getRankRRDConstrain: delay_RRD due to different bank group is: " << delay_RRD << std::endl;
            }
        }

        this->last_bank_id = bank_id;
        this->last_bankGroup_id = bankgroup_id;
    }

    earliest_time = std::max(time, std::max(time + delay_RRD, time + delay_FAW));
    delay = earliest_time - time;
    this->setLastACTTime(earliest_time);
    this->FAW.push_back(earliest_time);

    return delay;
}

uint64_t Rank::getRankFAWConstrain(uint64_t bankgroup_id, uint64_t bank_id, uint64_t time)
{
    if (bankgroup_id >= m_numBankGroups || bank_id >= m_numBanks)
    {
        std::cerr << "Error: Invalid bank or bank group ID: " << " Bank Group ID: " << bankgroup_id
                  << ", Bank ID: " << bank_id << std::endl;
        throw std::out_of_range("Rank::addACT Invalid bank group or bank ID");
    }

    uint64_t delay = 0;

    // 排空内容
    while (!FAW.empty() && FAW.front() + amp->getTFAW() <= time)
    {
        // 如果FAW队列不为空且满足FAW条件，移除最早的元素
        FAW.pop_front();
    }

    if (FAW.size() < 4)
    {
        // FAW.push_back(time);
        std::cout << "Add act to FAW. " << std::endl;
    }
    else
    {
        std::cout << "FAW is full, cannot add act." << std::endl;
        delay = (FAW.front() + amp->getTFAW()) - time;
        FAW.pop_front();
        // FAW.push_back(time + delay);
        std::cout << "FAW is full, delay is: " << delay << " And new window start time is: " << time + delay << std::endl;
    }

    return delay;
}
